﻿#region Header File
//
// ByteChannel.cs - Discrete channel (256-ary) with errors
//
// Copyright (C) Javier Valcarce. BSD License
#endregion

#region Using Statements
using System;
using System.Threading;
using System.IO;
using System.IO.Ports;
#endregion


public class ByteChannel : IDisposable
{
    // AVOID STATISTICAL DEPENDENCE:
    // the random source must be provided by the user, this is to avoid statistical  dependence
    // between the errors produced by diferent instances of this class. To make a static random
    // generator common to all instances is again not a good solution because then would be 
    // dependence with other stocastic processes in the system.
    Random rnd0;
    Thread thr1;

    public double ByteErrorRate { get; private set; }
    public Stream I             { get; private set; }
    public Stream O             { get; private set; }

    /// <summary>Number of bytes relayed</summary>
    public long ByteCount  { get; private set; }
    /// <summary>Number of errors introduced</summary>
    public long ErrorCount { get; private set; }


    /// <summary>
    /// </summary>
    /// <param name="pe">Probability of error</param>
    /// <param name="istream">Input  stream</param>
    /// <param name="ostream">Output stream</param>
    public ByteChannel(Random rnd, double pe, Stream istream, Stream ostream)
    {
        rnd0 = rnd;
        ByteErrorRate = pe;
        ByteCount     = 0;
        ErrorCount    = 0;
        I = istream;
        O = ostream;

        if (pe < 0.0)    throw new ArgumentException();
        if (pe > 1.0)    throw new ArgumentException();
        if (!I.CanRead ) throw new ArgumentException();
        if (!O.CanWrite) throw new ArgumentException();
       
        thr1  = new Thread(RelayerThread);
        thr1.IsBackground = true;
        thr1.Start();
    }

    // Ends the relayer thread
    public void Dispose()
    {
        thr1.Interrupt();
    }


    public void RelayerThread()
	{
        byte[] b = new byte[1];
        byte   n;
        double s;        

        while (true)
        {
            try
            {
                // compute next error sample
                lock (rnd0)
                {
                    s = rnd0.NextDouble();
                    if (s < ByteErrorRate)
                    {
                        // error 0x00 represents no error and is not valid
                        ErrorCount++;
                        n = (byte) rnd0.Next(1, 256);
                    }
                    else
                    {
                        n = 0;
                    }
                }

                I.Read (b, 0, 1);

                // simulate transmission error 
                b[0] ^= n;

                O.Write(b, 0, 1);
                ByteCount++;
            }
            catch (ThreadInterruptedException )
            {
                // 
                break;
            }
        }
	}

   
}
